package com.retrofit.server.processor;

import com.retrofit.server.processor.AnnotationClassMethod.MethodHandlerParam;
import com.retrofit.server.processor.AnnotationClassMethod.MethodParam;
import com.squareup.javapoet.ClassName;
import com.squareup.javapoet.CodeBlock;
import com.squareup.javapoet.FieldSpec;
import com.squareup.javapoet.JavaFile;
import com.squareup.javapoet.MethodSpec;
import com.squareup.javapoet.ParameterSpec;
import com.squareup.javapoet.TypeName;
import com.squareup.javapoet.TypeSpec;
import com.squareup.javapoet.TypeVariableName;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.annotation.processing.ProcessingEnvironment;
import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.AnnotationValue;
import javax.lang.model.element.Element;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.Modifier;

/**
 * @author GYL
 * @dateTime 2020/4/24-15:05
 * @description 接口实现类生成
 */
public class JavaFileGenerate {

  private static final String SUFFIX = "Service";

  private String pkgName;
  private TypeSpec.Builder mTypeSpceBuilder;
  private MethodSpec.Builder mMethodSpceBuilder;

  public JavaFileGenerate generateClass(AnnotationClass annotationClass){
    if(mTypeSpceBuilder==null) {
      pkgName = annotationClass.getAnnotatedPkgName();
      CodeBlock.Builder javaDocBuilder=CodeBlock.builder();
      javaDocBuilder.add("\n")
          .add("@author GYL\n")
          .add("@dateTime $N\n",getNowDateTime())
          .add("@description  $N:接口封装实现类\n",annotationClass.getAnnotatedSimpleName())
          .add("@see $N\n\n",annotationClass.getAnnotatedSimpleName());
      mTypeSpceBuilder = TypeSpec.classBuilder(annotationClass.getAnnotatedSimpleName() + SUFFIX)
          .addModifiers(Modifier.PUBLIC)
                           //生成内部service 变量
                           .addField(generateFiled(pkgName, annotationClass.getAnnotatedSimpleName()))
                           //生成What 计数器
                           .addField(generateCountWhatFiled())
                           //生成内部Service实例
                           .addMethod(generateMethod(pkgName, annotationClass.getAnnotatedSimpleName()))
                           .addMethod(generateCountWhatMethod())
                           .addJavadoc(javaDocBuilder.build());

      //添加接口方法
      List<AnnotationClassMethod> methodList = annotationClass.getMethodList();
      if(methodList!=null){
        for (AnnotationClassMethod classMethod : methodList) {
          MethodSpec methodSpec=generateMethodImpl(classMethod);
          mTypeSpceBuilder.addMethod(methodSpec);
        }
      }
    }
    return this;
  }

  private String getNowDateTime(){
    SimpleDateFormat sdf=new SimpleDateFormat("yyyyMMdd HH:mm");
    return sdf.format(new Date(System.currentTimeMillis()));
  }


  /**
   * 生成Java 类文件
   * @param processingEnv
   */
  public void generateJavaFile(ProcessingEnvironment processingEnv) {
    if(mTypeSpceBuilder!=null){
      JavaFile javaFile = JavaFile.builder(pkgName, mTypeSpceBuilder.build())
          .skipJavaLangImports(true)
          .addFileComment("Retrofit Api Interface implement class,auto generation")
          .build();
      try {
        //javaFile.writeTo(System.out);
        javaFile.writeTo(processingEnv.getFiler());
      } catch (IOException e) {
        e.printStackTrace();
      }
    }
  }

  public static void checkValidClass() throws ProcessorException{

  }

  
  /**
   * 生成公共的 _innerCall 属性
   * @param pkgName 接口服务包名
   * @param clzzName 接口服务类名
   * @return
   */
  private FieldSpec generateFiled(String pkgName,String clzzName) {
    ClassName elClazz=ClassName.get(pkgName,clzzName);
    FieldSpec.Builder fb = FieldSpec.builder(elClazz, "_innerCall", Modifier.PRIVATE, Modifier.STATIC);
    return fb.build();
  }
  
  /**
   * 创建公共请求类 init 方法
   */
  private MethodSpec generateMethod(String pkgName,String clzzName) {
    ClassName elClazz=ClassName.get(pkgName,clzzName);
    MethodSpec.Builder mb = MethodSpec.methodBuilder("innerCall")
                              .addModifiers(Modifier.STATIC, Modifier.PRIVATE)
                              .addJavadoc("创建Api服务\n")
                              .returns(elClazz)
                              .addCode("if(_innerCall==null){\n"
                                         + "$W$W_innerCall=com.okhttp.retrofit.RetrofitClient.getInstance().createService($T.class);"
                                         + "\n}\n"
                                         + "return _innerCall;\n", elClazz);
    return mb.build();
  }
  
  /**
   * 生成 what计数器
   * @return
   */
  private FieldSpec generateCountWhatFiled(){
    //static final AtomicInteger sNextGeneratedId = new AtomicInteger(100);
    ClassName className=ClassName.get("java.util.concurrent.atomic","AtomicInteger");
    FieldSpec.Builder fb=FieldSpec.builder(className,"sNextGeneratedId")
      .addModifiers(Modifier.PRIVATE,Modifier.STATIC,Modifier.FINAL)
      .initializer("new AtomicInteger(10)")
      .addJavadoc("生成接口唯一码（WHAT）\n");
    return fb.build();
  }
  
  /**
   * 生成What
   * @return
   */
  private MethodSpec generateCountWhatMethod(){
    //  private static int generateWhat() {
    //    for (;;) {
    //      final int result = sNextGeneratedId.get();
    //      // aapt-generated IDs have the high byte nonzero; clamp to the range
    //      // under that.
    //      int newValue = result + 1;
    //      if (newValue > 0x00FFFFFF) {
    //        // Roll over to 1, not 0.
    //        newValue = 1;
    //      }
    //      if (sNextGeneratedId.compareAndSet(result, newValue)) {
    //        return result;
    //      }
    //    }
    //  }
    ClassName className = ClassName.get("java.util.concurrent.atomic", "AtomicInteger");
    MethodSpec.Builder mb = MethodSpec.methodBuilder("generateWhat")
                              .addModifiers(Modifier.STATIC, Modifier.PRIVATE)
                              .addJavadoc("生成接口唯一码(What)\n")
                              .returns(TypeName.INT)
                              .addCode("for (;;) {\n")
                              .addCode("$W final int result = sNextGeneratedId.get();\n")
                              .addCode("$W$W // aapt-generated IDs have the high byte nonzero; clamp to the range under that.\n")
                              .addCode("$W$W$W int newValue = result + 1;\n")
                              .addCode("$W$W$W if (newValue > 0x00FFFFFF) {\n")
                              .addCode("$W$W$W // Roll over to 1, not 0.\n")
                              .addCode("$W$W$W newValue = 1;\n}\n")
                              .addCode("$W if (sNextGeneratedId.compareAndSet(result, newValue)) {\n")
                              .addCode("$W return result;$W \n}\n}");
    return mb.build();
    
  }


  /**
   * 实现接口服务中的方法
   * @param classMethod
   */
  public MethodSpec generateMethodImpl(AnnotationClassMethod classMethod){
      MethodSpec.Builder methodBuilder=MethodSpec.methodBuilder(classMethod.getMethodName())
          .addModifiers(Modifier.STATIC,Modifier.PUBLIC)
          .addJavadoc(classMethod.getCodeBlockJavadoc());
        //添加参数
      if(classMethod.getParams()!=null){
        for (MethodParam methodParam : classMethod.getParams()) {
          TypeName typeName;
          if(methodParam.typeMirror!=null){
            typeName=TypeName.get(methodParam.typeMirror);
            methodBuilder.addParameter(typeName,methodParam.paramName);
          }
        }
      }
    //添加回调参数：泛型 -- 多个泛型
    if(classMethod.getMethodHandlerParam()!=null){
      MethodHandlerParam methodHandlerParam = classMethod.getMethodHandlerParam();
      ParameterSpec parameterSpec=ParameterSpec.builder(methodHandlerParam.parameterizedTypeName,methodHandlerParam.methodParamName)
          .build();
      methodBuilder.addParameter(parameterSpec);
    }
    //添加代码块
    if (classMethod.getCodeBlock() != null) {
      methodBuilder.addCode(classMethod.getCodeBlock());
    }
    if(classMethod.getMethodReturn()!=null){
      if(!StringUtils.isEmpty(classMethod.getMethodReturn().typeVariableName)) {
        methodBuilder
            .addTypeVariable(TypeVariableName.get(classMethod.getMethodReturn().typeVariableName));
      }
      if(classMethod.getMethodReturn().typeName==null){
        methodBuilder.returns(Void.class);
      }else{
        methodBuilder.returns(classMethod.getMethodReturn().typeName);
      }
    }
    return methodBuilder.build();
  }

  
  /**
   * 截取包名和类名
   */
  public String[] splitPackage$Class(String type) {
    String clazzName = type.substring(type.lastIndexOf(".") + 1, type.length());
    String packageName = type.substring(0, type.lastIndexOf('.'));
    return new String[]{packageName, clazzName};
  }

  /**
   * 获取注解的值 ，可为空
   * @param annotationName 注解的类名
   * @param filter 值的key
   */
  protected AnnotationValue getAnnotationValue(Element el, String annotationName, String filter) {
    //获取方法的Annotations
    List<? extends AnnotationMirror> ams = el.getAnnotationMirrors();
    for (AnnotationMirror am : ams) {
      log("annotationMirror:" + am.toString());
      log("annotationMirror:" + am.getAnnotationType());
      if (am.getAnnotationType().toString().endsWith(annotationName)) {
        Map<? extends ExecutableElement, ? extends AnnotationValue> map = am.getElementValues();
        Set<? extends ExecutableElement> setEl = map.keySet();
        for (ExecutableElement exel : setEl) {
          //获取Annotation的值
          if (exel.getSimpleName().toString().equals(filter)) {
            AnnotationValue anvalue = map.get(exel);
            return anvalue;
          }
        }
      }
    }
    return null;
  }
  
  
  protected void log(Object msg) {
    Print.log(String.format("%: >>> %s", getClass().getName(), String.valueOf(msg)));
  }
  
}
